﻿// ***********************************************************************
// Assembly         : TCPServerSocket
// Author           : yubao
// Created          : 07-18-2014
//
// Last Modified By : yubao
// Last Modified On : 07-30-2014
// ***********************************************************************
// <copyright file="ZLANClient.cs" company="www.cnblogs.com/dotcpp">
//     Copyright (c) www.cnblogs.com/dotcpp. All rights reserved.
// </copyright>
// <summary>客户端类，用于处理数据接收等</summary>
// ***********************************************************************


using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using WPFExcelReport.Helpers;
namespace WPFExcelReport
{
    public partial class ZLANClient
    {
        public bool Alive { get; set; }
        public IPEndPoint IpEndPoint { get; private set; }

        private readonly Socket SocketClient;

        private readonly byte[] received;
        private int _bytesRead;
        ApplicationBase app
        {
            get
            {
                return ApplicationBase.Instance;
            }
        }
        public Dictionary<string, object> ENVMAIN
        {
            get
            {
                return app.ENVMAIN(app.ServiceUri);
            }
        }
        public AMServer VSP
        {
            get
            {
                if (!ENVMAIN.ContainsKey("VSP"))
                    return null;
                return (AMServer)ENVMAIN["VSP"];
            }
        }
        public CommandQ CQ;
        public VICC_ZLAN VZL
        {
            get
            {
                if (!ENVMAIN.ContainsKey("VZL"))
                    return null;
                return (VICC_ZLAN)ENVMAIN["VZL"];
            }
        }
        public DataTable TBZLAM
        {
            get;
            set;
        }
        public DataTable TBZL
        {
            get;
            set;
        }

        public string EndPoint
        {
            get;
            set;
        }
        public string PLCIP
        {
            get;
            set;
        }
        bool _IsPortReaday = false;
        public bool IsPortReaday
        {
            get
            {
                return _IsPortReaday;
            }
            set
            {
                _IsPortReaday = value;
            }
        }

        bool _IsMessageHandling = false;
        public bool IsMessageHandling
        {
            get
            {
                return _IsMessageHandling;
            }
            set
            {
                _IsMessageHandling = value;
            }
        }
        string filter;
        DataRow[] rows;
        bool IsRun = false;
        public Queue<CommandQ> SendQ = new Queue<CommandQ>();
        public Thread receiveThread;
        public ZLANClient(Socket _clientSocket)
        {
            SocketClient = _clientSocket;

            IpEndPoint = SocketClient.RemoteEndPoint as IPEndPoint;
            EndPoint = IpEndPoint.ToString();
            int idx = EndPoint.IndexOf(':');
            PLCIP = EndPoint.Substring(0, idx);
            received = new byte[1024];
            Alive = true;
            receiveThread = new Thread(ReceiveMessage);
            receiveThread.Name = Guid.NewGuid().ToString();
            receiveThread.IsBackground = true;
            receiveThread.Priority = ThreadPriority.BelowNormal;
            receiveThread.Start();
        }
        public bool GetDevTyp()
        {
            bool IsLogged = false;
            string filter = "LOCALIP='" + PLCIP + "'";
            DataRow[] rows = TBZLAM.Select(filter);
            if (rows.Length > 0)
            {
                IsLogged = true;
            }
            else
            {
                VZL.ADDZLAN(PLCIP);
            }
            return IsLogged;
        }
        public void ZLAUTOREAD()
        {
            try
            {
                if (IsRun)
                    return;
                DataSet SResult = VZL.ZLAUTOREAD(PLCIP);
                DataTable TBAM06 = SResult.Tables[0].Copy();
                SetModeCount(TBAM06);
                //01 10 00 8C 00 01 02 A8 01 07 5C
                foreach (DataRow row in TBAM06.Rows)
                {
                    string cmd = row["AMEMESCHL"].ToString().Trim();
                    string devtyp = row["DEVTYP"].ToString().Trim();
                    string method = row["METHOD"].ToString().Trim();
                    string address = row["AMEWERTVON"].ToString();
                    string count = row["AMEWERTBIS"].ToString();
                    string SlaveID = row["AMEMODNR"].ToString();
                    string Channel = row["AMEEINSTNR"].ToString();
                    switch (devtyp)
                    {
                        case "FXPLC":
                            cmd = "0";
                            break;
                        case "S7200":
                            cmd = "RD";//读取而已
                            break;
                        case "AM3011":
                            cmd = "R04";
                            break;
                        default:
                            break;
                    }
                    CommandQ cq = new CommandQ();
                    cq.ip = PLCIP;
                    cq.command = cmd;
                    cq.ADDRESS = address;
                    cq.count = count;
                    cq.DataSend = "";
                    cq.SlaveID = SlaveID.Substring(1);
                    cq.Channel = Channel.Substring(1);
                    cq.devtyp = devtyp;
                    cq.MSETID = row["BDEKEY"].ToString().Trim();
                    cq.BDEKEN = row["AMEBDEKEN"].ToString().Trim();
                    cq.INTERVAL = int.Parse(row["INTERVAL"].ToString());
                    SendQ.Enqueue(cq);
                    AutoRun();
                }
            }
            catch (Exception ex)
            {
                Helpers.LogHelper.error(ex.Message);
            }
        }
        public void SetModeCount(DataTable TBAM06)
        {
            int idx = 0;
            int startaddress = 140;
            UInt16[] _registerData = new UInt16[16];
            foreach (DataRow row in TBAM06.Rows)
            {
                string cmd = row["AMEMESCHL"].ToString().Trim();
                string devtyp = row["DEVTYP"].ToString().Trim();
                string method = row["METHOD"].ToString().Trim();
                string address = row["AMEWERTVON"].ToString();
                string count = row["AMEWERTBIS"].ToString();
                string SlaveID = row["AMEMODNR"].ToString();
                string Channel = row["AMEEINSTNR"].ToString();
                if (devtyp != "AM3011")
                    continue;
                CQ.ip = PLCIP;
                CQ.command = "W10";
                CQ.ADDRESS = (startaddress + idx).ToString();
                CQ.count = 1.ToString();
                _registerData = new UInt16[16];
                _registerData[0] = 10 * 256 * 16 + 8 * 256 + 0 * 16 + 1;
                CQ.DataSend = _registerData;
                CQ.SlaveID = SlaveID.Substring(1);
                CQ.Channel = Channel.Substring(1);
                CQ.devtyp = devtyp;
                CQ.MSETID = row["BDEKEY"].ToString().Trim();
                CQ.BDEKEN = row["AMEBDEKEN"].ToString().Trim();
                CQ.INTERVAL = int.Parse(row["INTERVAL"].ToString());
                Send(CQ.devtyp, CQ.command, CQ.SlaveID, CQ.ADDRESS, CQ.count, CQ.DataSend);
                CQ = new CommandQ();
                CQ.ip = PLCIP;
                CQ.command = "W10";
                CQ.ADDRESS = idx * 2;
                CQ.count = 2.ToString();
                _registerData = new UInt16[16];
                _registerData[0] = 0 * 256 * 16 + 0 * 256 + 0 * 16 + 0;
                _registerData[1] = 0 * 256 * 16 + 0 * 256 + 0 * 16 + 0;
                CQ.DataSend = _registerData;
                CQ.SlaveID = SlaveID.Substring(1);
                CQ.Channel = Channel.Substring(1);
                CQ.devtyp = devtyp;
                CQ.MSETID = row["BDEKEY"].ToString().Trim();
                CQ.BDEKEN = row["AMEBDEKEN"].ToString().Trim();
                CQ.INTERVAL = int.Parse(row["INTERVAL"].ToString());
                Send(CQ.devtyp, CQ.command, CQ.SlaveID, CQ.ADDRESS, CQ.count, CQ.DataSend);
                CQ = new CommandQ();
                CQ.ip = PLCIP;
                CQ.command = "W03";
                CQ.ADDRESS = 176.ToString();
                CQ.count = 1.ToString();
                CQ.DataSend = "";
                CQ.SlaveID = SlaveID.Substring(1);
                CQ.Channel = Channel.Substring(1);
                CQ.devtyp = devtyp;
                CQ.MSETID = row["BDEKEY"].ToString().Trim();
                CQ.BDEKEN = row["AMEBDEKEN"].ToString().Trim();
                CQ.INTERVAL = int.Parse(row["INTERVAL"].ToString());
                Send(CQ.devtyp, CQ.command, CQ.SlaveID, CQ.ADDRESS, CQ.count, CQ.DataSend);
                CQ = new CommandQ();
                CQ.ip = PLCIP;
                CQ.command = "W10";
                CQ.ADDRESS = 176.ToString();
                CQ.count = 1.ToString();
                _registerData = new UInt16[16];
                _registerData[0] = 0 * 256 * 16 + 0 * 256 + 0 * 16 + 15;
                CQ.DataSend = _registerData;
                CQ.SlaveID = SlaveID.Substring(1);
                CQ.Channel = Channel.Substring(1);
                CQ.devtyp = devtyp;
                CQ.MSETID = row["BDEKEY"].ToString().Trim();
                CQ.BDEKEN = row["AMEBDEKEN"].ToString().Trim();
                CQ.INTERVAL = int.Parse(row["INTERVAL"].ToString());
                Send(CQ.devtyp, CQ.command, CQ.SlaveID, CQ.ADDRESS, CQ.count, CQ.DataSend);
                idx++;
            }
        }
        public void AutoRun()
        {
            try
            {
                ThreadStart Work = null;
                Thread Thread = null;
                if (IsRun)
                    return;
                Work = new ThreadStart(DoSendWork);
                Thread = new Thread(Work);
                Thread.Priority = ThreadPriority.BelowNormal;
                Thread.Start();
            }
            catch (Exception ex)
            {
                // EventDispatcher.Instance.RaiseStatus("发送异常终止", StatusEventType.StopProgress);
                Helpers.LogHelper.error(ex.Message);
            }
        }
        SortedDictionary<long, CommandQ> TimingJob = new SortedDictionary<long, CommandQ>();
        void DoSendWork()
        {
            if (IsRun)
            {
                return;
            }
            ///若是首个任务，则启动运行，初始化端口为空闲
            IsRun = true;
            int count = 0;
            while (true)
            {
                if (!SocketClient.Connected)
                {
                    TimingJob = new SortedDictionary<long, CommandQ>();
                    SendQ = new Queue<CommandQ>();
                    break;
                }
                if (SendQ.Count == 0 && TimingJob.Count == 0)
                {
                    IsRun = false;
                    CloseConnection();
                    break;
                }
                if (SendQ.Count == 0 && TimingJob.Count > 0)
                {
                    long IsTakeOut = -1;
                    long now = DateTime.Now.ToFileTime();
                    long next = now;
                    double tosleep = 1;
                    if (TimingJob.Count > 0)
                    {
                        foreach (KeyValuePair<long, CommandQ> kp in TimingJob)
                        {
                            next = kp.Key;
                            if (next <= now)
                            {
                                IsTakeOut = kp.Key;
                                SendQ.Enqueue(kp.Value);
                            }
                            else
                            {
                                DateTime date2 = DateTime.FromFileTime(next);
                                TimeSpan ts = DateTime.FromFileTime(next).Subtract(DateTime.FromFileTime(now));
                                tosleep = ts.Hours * 60 * 60 + ts.Minutes * 60 + ts.Seconds;
                                if (tosleep < 1)
                                    tosleep = 5;
                                Thread.Sleep(System.Convert.ToInt32(tosleep) * 1000);
                            }
                            break;
                        }
                        if (IsTakeOut > 0)
                        {
                            TimingJob.Remove(IsTakeOut);
                        }
                    }
                    continue;
                }
                CQ = SendQ.Dequeue();
                try
                {

                    if (CQ.INTERVAL > 0)
                    {
                        long next = DateTime.Now.AddSeconds(CQ.INTERVAL).ToFileTime();
                        TimingJob[next] = CQ;
                    }
                    else
                    {
                        Helpers.LogHelper.error(time + ":" + PLCIP + CQ.SlaveID + " INTERVAL==0");
                    }
                    IsPortReaday = false;
                    count = 0;
                    Send(CQ.devtyp, CQ.command, CQ.SlaveID, CQ.ADDRESS, CQ.count, CQ.DataSend);
                    Thread.Sleep(300);
                }
                catch (Exception ex)
                {
                    Helpers.LogHelper.error(ex.StackTrace + ex.Message);
                }
                while (!IsPortReaday)
                {
                    if (!IsMessageHandling)
                    {
                        count++;
                    }
                    Thread.Sleep(300);
                    if (count > 1)
                    {
                        if (!FailedCommand.ContainsKey(CQ.MSETID))
                        {
                            FailedCommand.Add(CQ.MSETID, 1);
                        }
                        else
                        {
                            FailedCommand[CQ.MSETID] = FailedCommand[CQ.MSETID] + 1;
                        }
                        IsPortReaday = true;
                        IsMessageHandling = false;
                        Helpers.LogHelper.error(time + ":" + PLCIP + CQ.SlaveID + " CLS");
                        //CloseConnection();
                        break;
                    }
                }
                if (SendQ.Count == 0 && TimingJob.Count == 0)
                {
                    IsRun = false;
                    break;
                }
                string removedkeys = "";
                var qrmv = from fc in FailedCommand
                           where fc.Value > 1
                           select fc;
                foreach (var kp in qrmv)
                {
                    removedkeys = kp.Key;
                    List<long> keys = new List<long>();
                    foreach (KeyValuePair<long, CommandQ> tkp in TimingJob)
                    {
                        if (tkp.Value.MSETID == kp.Key)
                        {
                            keys.Add(tkp.Key);
                        }
                    }
                    foreach (long key in keys)
                    {
                        TimingJob.Remove(key);
                    }
                }
                FailedCommand.Remove(removedkeys);
            }
        }
        string date = "", time = "";

        Dictionary<string, int> FailedCommand = new Dictionary<string, int>();


        private void ProcessData(Action<string, byte[], int> recvDataAction)
        {
            if (_bytesRead > 0 && recvDataAction != null)
            {
                recvDataAction(ToString(), received, _bytesRead);
                _bytesRead = 0;
            }
        }

        public void CloseConnection()
        {
            if (CQ.devtyp == "CC5D99RS")
            {
                LogHelper.info(CQ.devtyp + "," + PLCIP + " has problems");
                return;
            }
               
            if (CQ.devtyp == "YHA12")
            {
                LogHelper.info(CQ.devtyp + "," + PLCIP + " has problems");
                return;
            }
            if (CQ.devtyp == "HT1300")
            {
                LogHelper.info(CQ.devtyp + "," + PLCIP + " has problems");
                return;
            }
            if (CQ.devtyp == "XM05B3")
                return;
            if (CQ.devtyp == "")
            {
                LogHelper.info(CQ.devtyp + "," + PLCIP + " has problems");
                return;
            }
            //LogHelper.info(CQ.devtyp +","+PLCIP + " is closing by server");
            return;
            SocketClient.Close();
            Alive = false;
            Thread.Sleep(3000);
        }

        public void SendMessage(byte[] buffer)
        {
            if (!Alive) return;
            try
            {
                SocketClient.Send(buffer);
                //ClientStream.Write(buffer, 0, buffer.Length);
            }
            catch (Exception ex)
            {
                LogHelper.error(ex.StackTrace + ex.Message);
                //CloseConnection();
            }
        }
        public void ZLSEND(Dictionary<string, object> pm)
        {
            try
            {
                string MSETID = (string)pm["MSETID"];
                string CHANNEL = (string)pm["CHANNEL"];
                string VALUE = (string)pm["VALUE"];
                filter = "TRIM(AMEBDEKEN) ='" + CHANNEL.Trim() + "'";
                rows = TBZL.Select(filter);
                if (rows.Length == 0)
                {
                    throw new Exception("Please check the listening status～～");
                }
                string ip = rows[0]["LOCALIP"].ToString().Trim();

                foreach (DataRow row in rows)
                {
                    string cmd = row["AMEMESCHL"].ToString().Trim();
                    string address = row["AMEWERTVON"].ToString();
                    string count = row["AMEWERTBIS"].ToString();
                    string SlaveID = row["AMEMODNR"].ToString();
                    string Channel = row["AMEEINSTNR"].ToString();
                    string devtyp = row["DEVTYP"].ToString().Trim();
                    string method = row["METHOD"].ToString().Trim();
                    switch (devtyp)
                    {
                        case "FXPLC":
                            cmd = "0";
                            break;
                        case "AM3011":
                            cmd = "R04";
                            break;
                        case "S7200":
                            if (cmd == "QW")
                                cmd = "Q_Word";
                            break;
                    }
                    CommandQ cq = new CommandQ();
                    cq.ip = ip;
                    cq.devtyp = devtyp;
                    cq.command = cmd;
                    cq.ADDRESS = address;
                    cq.count = count;
                    cq.DataSend = VALUE;
                    cq.INTERVAL = 0;
                    cq.SlaveID = SlaveID.Substring(1);
                    cq.Channel = Channel.Substring(1);
                    cq.MSETID = MSETID;
                    SendQ.Enqueue(cq);
                    AutoRun();
                }
            }
            catch (Exception ex)
            {
                Helpers.LogHelper.error(ex.Message);
            }
        }
        public override string ToString()
        {
            return IpEndPoint.Address.ToString();
        }

        public bool IsConnected
        {
            get
            {
                try
                {
                    return SocketClient.Connected;
                    /*
                    return !(_clientSocket.Client.Poll(1, SelectMode.SelectRead) 
                        && _clientSocket.Available == 0);*/
                }
                catch (SocketException e)
                {
                    return false;
                }
            }
        }
    }
}
